﻿using System;
using System.Collections.Generic;
using UnityEngine.Events;
using Object = System.Object;
namespace TTUnityPlugin {
public delegate void NotificationDelegate(params object[] param);

// Copy from ugui
class NotificationObjectPool<T> where T : new() {
    private readonly Stack<T> m_Stack = new Stack<T>();
    private readonly UnityAction<T> m_ActionOnGet;
    private readonly UnityAction<T> m_ActionOnRelease;

    public int countAll { get; private set; }
    public int countActive { get { return countAll - countInactive; } }
    public int countInactive { get { return m_Stack.Count; } }

    public NotificationObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease) {
        m_ActionOnGet = actionOnGet;
        m_ActionOnRelease = actionOnRelease;
    }

    public T Get() {
        T element;
        if(m_Stack.Count == 0) {
            element = new T();
            countAll++;
        } else {
            element = m_Stack.Pop();
        }
        if(m_ActionOnGet != null)
            m_ActionOnGet(element);
        return element;
    }

    public void Release(T element) {
        if(m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
            UnityEngine.Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
        if(m_ActionOnRelease != null)
            m_ActionOnRelease(element);
        m_Stack.Push(element);
    }
}

class NotificationObserver {
    public string name;
    public object target;
    public NotificationDelegate selector;
    public NotificationObserver() {
    }

    public NotificationObserver(string _name, object _target, NotificationDelegate _selector) {
        SetAttr(_name,  _target,  _selector);
    }
    public void SetAttr(string _name, object _target, NotificationDelegate _selector) {
        this.name = _name;
        this.target = _target;
        this.selector = _selector;
    }

    public void PerformSelector(params object[] param) {
        if(target != null) {
            selector.Invoke(param);
        }
    }

    public void Reset() {
        this.name = "";
        this.target = null;
        this.selector = null;
    }
}

public class NotificationCenter {
    private static NotificationCenter instance;
    private static NotificationObjectPool<NotificationObserver> observers = new NotificationObjectPool<NotificationObserver>(null, x => x.Reset());
    // 注意相同name 相同target 只能有一个Observer
    private Dictionary<string, List<NotificationObserver>> dict;
    public NotificationCenter() {
        dict = new Dictionary<string, List<NotificationObserver>>();
    }

    public static NotificationCenter Share() {
        if(instance == null) {
            instance = new NotificationCenter();
        }
        return instance;
    }

  //  private readonly List<NotificationObserver> tmpObserver = new List<NotificationObserver>();

    public void PostNotification(string name, params object[] obj) {
        List<NotificationObserver> tmpObserver = LTListPool<NotificationObserver>.Get();
        tmpObserver.Clear();
        List<NotificationObserver> delegateList;
        if(!dict.TryGetValue(name, out delegateList)) {
            return;
        }
        int count = delegateList.Count;
        if(count > 0) {
            tmpObserver.AddRange(delegateList);
        }
        for(int i = 0; i < count; ++i) {
            NotificationObserver handler = tmpObserver[i];
            handler.PerformSelector(obj);
        }
        tmpObserver.Clear();
        LTListPool<NotificationObserver>.Release(tmpObserver);
    }

    public void PostNotification(string name) {
        this.PostNotification(name, null);
    }

    public void AddObserver(object target, NotificationDelegate selector, string name) {
        AddObserver(name, target, selector);
    }

    public void AddObserver(string name, object target, NotificationDelegate selector) {
        List<NotificationObserver> delegateList;
        if(!dict.TryGetValue(name, out delegateList)) {
            delegateList = new List<NotificationObserver>();
            NotificationObserver observer = observers.Get();
            observer.SetAttr(name, target, selector);
            delegateList.Add(observer);
            dict.Add(name, delegateList);
        } else {
            for(int i = delegateList.Count - 1; i >= 0; i--) {
                if(delegateList[i].name == name && delegateList[i].target == target) {
                    DebugUtil.LogWarning(string.Format("Notification {0} already had with the target {1}", name, target));
                    return;
                }
            }
            NotificationObserver observer = observers.Get();
            observer.SetAttr(name, target, selector);
            delegateList.Add(observer);
        }
    }

    public void  RemoveObserver(object target, string name) {
        RemoveObserver(name, target);
    }
    public void RemoveObserver(string name, object target) {
        List<NotificationObserver> delegateList;
        if(!dict.TryGetValue(name, out delegateList)) {
            return;
        }
        for(int i = delegateList.Count - 1; i >= 0; i--) {
            if(delegateList[i].name == name && delegateList[i].target == target) {
                observers.Release(delegateList[i]);
                delegateList.RemoveAt(i);
                break;
            }
        }
    }

    public void RemoveObservers(string name) {
        List<NotificationObserver> delegateList;
        if(!dict.TryGetValue(name, out delegateList)) {
            return;
        }
        for(int i = delegateList.Count - 1; i >= 0; i--) {
            observers.Release(delegateList[i]);
        }
        delegateList.Clear();
        dict.Remove(name);
    }

    public void RemoveAllObservers() {
        var iter = dict.GetEnumerator();
        while(iter.MoveNext()) {
            List<NotificationObserver> delegateList = iter.Current.Value;
            for(int i = delegateList.Count - 1; i >= 0; i--) {
                observers.Release(delegateList[i]);
            }
        }
        dict.Clear();
    }
}
}